home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / SimpleTimeZone.java < prev    next >
Text File  |  1998-09-22  |  31KB  |  739 lines

  1. /*
  2.  * @(#)SimpleTimeZone.java    1.16 98/01/12
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.util;
  32.  
  33. import java.io.ObjectInputStream;
  34. import java.io.ObjectOutputStream;
  35. import java.io.IOException;
  36.  
  37. /**
  38.  * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
  39.  * that represents a time zone for use with a Gregorian calendar. This
  40.  * class does not handle historical changes.
  41.  *
  42.  * <P>
  43.  * Use a negative value for <code>dayOfWeekInMonth</code> to indicate that
  44.  * <code>SimpleTimeZone</code> should count from the end of the month backwards.
  45.  * For example, Daylight Savings Time ends at the last
  46.  * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
  47.  *
  48.  * @see      Calendar
  49.  * @see      GregorianCalendar
  50.  * @see      TimeZone
  51.  * @version  1.16 01/12/98
  52.  * @author   David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
  53.  */
  54. public class SimpleTimeZone extends TimeZone {
  55.     /**
  56.      * Constructs a SimpleTimeZone with the given base time zone offset from GMT
  57.      * and time zone ID. Timezone IDs can be obtained from
  58.      * TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to
  59.      * construct a TimeZone.
  60.      *
  61.      * @param rawOffset  The given base time zone offset to GMT.
  62.      * @param ID         The time zone ID which is obtained from
  63.      *                   TimeZone.getAvailableIDs.
  64.      */
  65.     public SimpleTimeZone(int rawOffset, String ID)
  66.     {
  67.         this.rawOffset = rawOffset;
  68.         setID (ID);
  69.     dstSavings = millisPerHour; // In case user sets rules later
  70.     }
  71.  
  72.     /**
  73.      * Construct a SimpleTimeZone with the given base time zone offset from
  74.      * GMT, time zone ID, time to start and end the daylight time. Timezone IDs
  75.      * can be obtained from TimeZone.getAvailableIDs. Normally you should use
  76.      * TimeZone.getDefault to create a TimeZone. For a time zone that does not
  77.      * use daylight saving time, do not use this constructor; instead you should
  78.      * use SimpleTimeZone(rawOffset, ID).
  79.      *
  80.      * By default, this constructor specifies day-of-week-in-month rules. That
  81.      * is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this
  82.      * indicates the first Sunday in the startMonth. A startDay of -1 likewise
  83.      * indicates the last Sunday. However, by using negative or zero values for
  84.      * certain parameters, other types of rules can be specified.
  85.      *
  86.      * Day of month. To specify an exact day of the month, such as March 1, set
  87.      * startDayOfWeek to zero.
  88.      *
  89.      * Day of week after day of month. To specify the first day of the week
  90.      * occurring on or after an exact day of the month, make the day of the week
  91.      * negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY,
  92.      * this indicates the first Monday on or after the 5th day of the
  93.      * startMonth.
  94.      *
  95.      * Day of week before day of month. To specify the last day of the week
  96.      * occurring on or before an exact day of the month, make the day of the
  97.      * week and the day of the month negative. For example, if startDay is -21
  98.      * and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or
  99.      * before the 21st of the startMonth.
  100.      *
  101.      * The above examples refer to the startMonth, startDay, and startDayOfWeek;
  102.      * the same applies for the endMonth, endDay, and endDayOfWeek.
  103.      *
  104.      * @param rawOffset       The given base time zone offset to GMT.
  105.      * @param ID              The time zone ID which is obtained from
  106.      *                        TimeZone.getAvailableIDs.
  107.      * @param startMonth      The daylight savings starting month. Month is
  108.      *                        0-based. eg, 0 for January.
  109.      * @param startDay        The daylight savings starting
  110.      *                        day-of-week-in-month. Please see the member
  111.      *                        description for an example.
  112.      * @param startDayOfWeek  The daylight savings starting day-of-week. Please
  113.      *                        see the member description for an example.
  114.      * @param startTime       The daylight savings starting time. Please see the
  115.      *                        member description for an example.
  116.      * @param endMonth        The daylight savings ending month. Month is
  117.      *                        0-based. eg, 0 for January.
  118.      * @param endDay          The daylight savings ending day-of-week-in-month.
  119.      *                        Please see the member description for an example.
  120.      * @param endDayOfWeek    The daylight savings ending day-of-week. Please
  121.      *                        see the member description for an example.
  122.      * @param endTime         The daylight savings ending time. Please see the
  123.      *                        member description for an example.
  124.      */
  125.     public SimpleTimeZone(int rawOffset, String ID,
  126.                           int startMonth, int startDay, int startDayOfWeek, int startTime,
  127.                           int endMonth, int endDay, int endDayOfWeek, int endTime)
  128.     {
  129.         this(rawOffset, ID,
  130.              startMonth, startDay, startDayOfWeek, startTime,
  131.              endMonth, endDay, endDayOfWeek, endTime,
  132.              millisPerHour);
  133.     }
  134.  
  135.     /**
  136.      * Constructor.  This constructor is package private at this point.  It will
  137.      * be made public at the next API change.  It is identical to the 10-argument
  138.      * constructor, but also takes a dstSavings parameter.
  139.      * @param dstSavings   The amount of time in ms saved during DST.
  140.      */
  141.     SimpleTimeZone(int rawOffset, String ID,
  142.                    int startMonth, int startDay, int startDayOfWeek, int startTime,
  143.                    int endMonth, int endDay, int endDayOfWeek, int endTime,
  144.                    int dstSavings)
  145.     {
  146.         setID(ID);
  147.         this.rawOffset      = rawOffset;
  148.         this.startMonth     = startMonth;
  149.         this.startDay       = startDay;
  150.         this.startDayOfWeek = startDayOfWeek;
  151.         this.startTime      = startTime;
  152.         this.endMonth       = endMonth;
  153.         this.endDay         = endDay;
  154.         this.endDayOfWeek   = endDayOfWeek;
  155.         this.endTime        = endTime;
  156.         this.dstSavings     = dstSavings;
  157.         // this.useDaylight    = true; // Set by decodeRules
  158.         decodeRules();
  159.     }
  160.  
  161.     /**
  162.      * Sets the daylight savings starting year.
  163.      *
  164.      * @param year  The daylight savings starting year.
  165.      */
  166.     public void setStartYear(int year)
  167.     {
  168.         startYear = year;
  169.     }
  170.  
  171.     /**
  172.      * Sets the daylight savings starting rule. For example, Daylight Savings
  173.      * Time starts at the first Sunday in April, at 2 AM in standard time.
  174.      * Therefore, you can set the start rule by calling:
  175.      * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
  176.      *
  177.      * @param month             The daylight savings starting month. Month is
  178.      *                          0-based. eg, 0 for January.
  179.      * @param dayOfWeekInMonth  The daylight savings starting
  180.      *                          day-of-week-in-month. Please see the member
  181.      *                          description for an example.
  182.      * @param dayOfWeek         The daylight savings starting day-of-week.
  183.      *                          Please see the member description for an
  184.      *                          example.
  185.      * @param time              The daylight savings starting time. Please see
  186.      *                          the member description for an example.
  187.      */
  188.     public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  189.                              int time)
  190.     {
  191.         startMonth = month;
  192.         startDay = dayOfWeekInMonth;
  193.         startDayOfWeek = dayOfWeek;
  194.         startTime = time;
  195.         // useDaylight = true; // Set by decodeRules
  196.         decodeRules();
  197.     }
  198.  
  199.     /**
  200.      * Sets the daylight savings ending rule. For example, Daylight Savings Time
  201.      * ends at the last (-1) Sunday in October, at 2 AM in standard time.
  202.      * Therefore, you can set the end rule by calling:
  203.      * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
  204.      *
  205.      * @param month             The daylight savings ending month. Month is
  206.      *                          0-based. eg, 0 for January.
  207.      * @param dayOfWeekInMonth  The daylight savings ending
  208.      *                          day-of-week-in-month. Please see the member
  209.      *                          description for an example.
  210.      * @param dayOfWeek         The daylight savings ending day-of-week. Please
  211.      *                          see the member description for an example.
  212.      * @param time              The daylight savings ending time. Please see the
  213.      *                          member description for an example.
  214.      */
  215.     public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  216.                            int time)
  217.     {
  218.         endMonth = month;
  219.         endDay = dayOfWeekInMonth;
  220.         endDayOfWeek = dayOfWeek;
  221.         endTime = time;
  222.         // useDaylight = true; // Set by decodeRules
  223.         decodeRules();
  224.     }
  225.  
  226.     /**
  227.      * Overrides TimeZone
  228.      * Gets offset, for current date, modified in case of
  229.      * daylight savings. This is the offset to add *to* UTC to get local time.
  230.      * Gets the time zone offset, for current date, modified in case of daylight
  231.      * savings. This is the offset to add *to* UTC to get local time. Assume
  232.      * that the start and end month are distinct, and that no rule refers to the
  233.      * end of February (e.g., last Sunday in February).
  234.      *
  235.      * @param era           The era of the given date.
  236.      * @param year          The year in the given date.
  237.      * @param month         The month in the given date. Month is 0-based. e.g.,
  238.      *                      0 for January.
  239.      * @param day           The day-in-month of the given date.
  240.      * @param dayOfWeek     The day-of-week of the given date.
  241.      * @param milliseconds  The millis in day in <em>standard</em> local time.
  242.      * @return              The offset to add *to* GMT to get local time.
  243.      */
  244.     public int getOffset(int era, int year, int month, int day, int dayOfWeek,
  245.                          int millis)
  246.     {
  247.         int result = rawOffset;
  248.  
  249.         // Bail out if we are before the onset of daylight savings time
  250.         if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result;
  251.  
  252.         // Check for southern hemisphere.  We assume that the start and end
  253.         // month are different.
  254.         boolean southern = (startMonth > endMonth);
  255.  
  256.         // Compare the date to the starting and ending rules.  For the ending
  257.         // rule comparison, we add the dstSavings to the millis passed in to convert
  258.         // them from standard to wall time.  +1 = date>rule, -1 = date<rule, 0 =
  259.         // date==rule.
  260.         int startCompare = compareToRule(month, day, dayOfWeek, millis,
  261.                                          startMode, startMonth, startDayOfWeek,
  262.                                          startDay, startTime);
  263.         int endCompare = compareToRule(month, day, dayOfWeek, millis + dstSavings,
  264.                                        endMode, endMonth, endDayOfWeek,
  265.                                        endDay, endTime);
  266.  
  267.         // Check for both the northern and southern hemisphere cases.  We
  268.         // assume that in the northern hemisphere, the start rule is before the
  269.         // end rule within the calendar year, and vice versa for the southern
  270.         // hemisphere.
  271.         if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
  272.             (southern && (startCompare >= 0 || endCompare < 0)))
  273.             result += dstSavings;
  274.  
  275.         return result;
  276.     }
  277.  
  278.     /**
  279.      * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
  280.      * on whether the date is after, equal to, or before the rule date. The
  281.      * millis are compared directly against the ruleMillis, so any
  282.      * standard-daylight adjustments must be handled by the caller. Assume that
  283.      * no rule references the end of February (e.g., last Sunday in February).
  284.      *
  285.      * @return  1 if the date is after the rule date, -1 if the date is before
  286.      *          the rule date, or 0 if the date is equal to the rule date.
  287.      */
  288.     private static int compareToRule(int month, int dayOfMonth, int dayOfWeek, int millis,
  289.                                      int ruleMode, int ruleMonth, int ruleDayOfWeek,
  290.                                      int ruleDay, int ruleMillis)
  291.     {
  292.         if (month < ruleMonth) return -1;
  293.         else if (month > ruleMonth) return 1;
  294.  
  295.         int ruleDayOfMonth = 0;
  296.         switch (ruleMode)
  297.         {
  298.         case DOM_MODE:
  299.             ruleDayOfMonth = ruleDay;
  300.             break;
  301.         case DOW_IN_MONTH_MODE:
  302.             // In this case ruleDay is the day-of-week-in-month
  303.             if (ruleDay > 0)
  304.                 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
  305.                     (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
  306.             else // Assume ruleDay < 0 here
  307.             {
  308.                 int monthLen = staticMonthLength[month];
  309.  
  310.                 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
  311.                     (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
  312.             }
  313.             break;
  314.         case DOW_GE_DOM_MODE:
  315.             ruleDayOfMonth = ruleDay +
  316.                 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
  317.             break;
  318.         case DOW_LE_DOM_MODE:
  319.             ruleDayOfMonth = ruleDay -
  320.                 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
  321.             // Note at this point ruleDayOfMonth may be <1, although it will
  322.             // be >=1 for well-formed rules.
  323.             break;
  324.         }
  325.  
  326.         if (dayOfMonth < ruleDayOfMonth) return -1;
  327.         else if (dayOfMonth > ruleDayOfMonth) return 1;
  328.  
  329.         if (millis < ruleMillis) return -1;
  330.         else if (millis > ruleMillis) return 1;
  331.         else return 0;
  332.     }
  333.  
  334.     /**
  335.      * Overrides TimeZone
  336.      * Gets the GMT offset for this time zone.
  337.      */
  338.     public int getRawOffset()
  339.     {
  340.         // The given date will be taken into account while
  341.         // we have the historical time zone data in place.
  342.         return rawOffset;
  343.     }
  344.  
  345.     /**
  346.      * Overrides TimeZone
  347.      * Sets the base time zone offset to GMT.
  348.      * This is the offset to add *to* UTC to get local time.
  349.      * Please see TimeZone.setRawOffset for descriptions on the parameter.
  350.      */
  351.     public void setRawOffset(int offsetMillis)
  352.     {
  353.         this.rawOffset = offsetMillis;
  354.     }
  355.  
  356.     /**
  357.      * Overrides TimeZone
  358.      * Queries if this time zone uses Daylight Savings Time.
  359.      */
  360.     public boolean useDaylightTime()
  361.     {
  362.         return useDaylight;
  363.     }
  364.  
  365.     /**
  366.      * Overrides TimeZone
  367.      * Queries if the given date is in Daylight Savings Time.
  368.      */
  369.     public boolean inDaylightTime(Date date)
  370.     {
  371.         GregorianCalendar gc = new GregorianCalendar(this);
  372.         gc.setTime(date);
  373.         return gc.inDaylightTime();
  374.     }
  375.  
  376.     /**
  377.      * Overrides Cloneable
  378.      */
  379.     public Object clone()
  380.     {
  381.         return super.clone();
  382.         // other fields are bit-copied
  383.     }
  384.  
  385.     /**
  386.      * Override hashCode.
  387.      * Generates the hash code for the SimpleDateFormat object
  388.      */
  389.     public synchronized int hashCode()
  390.     {
  391.         return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
  392.             endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
  393.     }
  394.  
  395.     /**
  396.      * Compares the equality of two SimpleTimeZone objects.
  397.      *
  398.      * @param obj  The SimpleTimeZone object to be compared with.
  399.      * @return     True if the given obj is the same as this SimpleTimeZone
  400.      *             object; false otherwise.
  401.      */
  402.     public boolean equals(Object obj)
  403.     {
  404.         if (this == obj)
  405.             return true;
  406.         if (!(obj instanceof SimpleTimeZone))
  407.             return false;
  408.  
  409.         SimpleTimeZone that = (SimpleTimeZone) obj;
  410.  
  411.         return this.getID().equals(that.getID()) &&
  412.             rawOffset == that.rawOffset &&
  413.             useDaylight == that.useDaylight &&
  414.             dstSavings == that.dstSavings &&
  415.             startMode == that.startMode &&
  416.             startMonth == that.startMonth &&
  417.             startDay == that.startDay &&
  418.             startDayOfWeek == that.startDayOfWeek &&
  419.             startTime == that.startTime &&
  420.             endMode == that.endMode &&
  421.             endMonth == that.endMonth &&
  422.             endDay == that.endDay &&
  423.             endDayOfWeek == that.endDayOfWeek &&
  424.             endTime == that.endTime &&
  425.             startYear == that.startYear;
  426.     }
  427.  
  428.     /**
  429.      * Return a string representation of this time zone.
  430.      * @return  a string representation of this time zone.
  431.      */
  432.     public String toString() {
  433.         return getClass().getName() +
  434.             "[id=" + getID() +
  435.             ",offset=" + rawOffset +
  436.             ",dstSavings=" + dstSavings +
  437.             ",useDaylight=" + useDaylight +
  438.             ",startYear=" + startYear +
  439.             ",startMode=" + startMode +
  440.             ",startMonth=" + startMonth +
  441.             ",startDay=" + startDay +
  442.             ",startDayOfWeek=" + startDayOfWeek +
  443.             ",startTime=" + startTime +
  444.             ",endMode=" + endMode +
  445.             ",endMonth=" + endMonth +
  446.             ",endDay=" + endDay +
  447.             ",endDayOfWeek=" + endDayOfWeek +
  448.             ",endTime=" + endTime + ']';
  449.     }
  450.  
  451.     // =======================privates===============================
  452.  
  453.     private int startMonth, startDay, startDayOfWeek, startTime;
  454.     private int endMonth, endDay, endDayOfWeek, endTime;
  455.     private int startYear;
  456.     private int rawOffset;
  457.     private boolean useDaylight=false; // indicate if this time zone uses DST
  458.     private static final int millisPerHour = 60*60*1000;
  459.     // WARNING: assumes that no rule is measured from the end of February,
  460.     // since we don't handle leap years. Could handle assuming always
  461.     // Gregorian, since we know they didn't have daylight time when
  462.     // Gregorian calendar started.
  463.     // monthLength was non-static in JDK 1.1, so we have to keep it that way
  464.     // to maintain serialization compatibility. However, there's no need to
  465.     // recreate the array each time we create a new time zone.
  466.     private final byte monthLength[] = staticMonthLength;
  467.     private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  468.  
  469.     /** 
  470.      * Variables specifying the mode of the start and end rules.
  471.      */
  472.     private int startMode, endMode; // fields new in JDK 1.1.4
  473.  
  474.     /**
  475.      * A positive value indicating the amount of time saved during DST in ms.
  476.      * Typically one hour; sometimes 30 minutes.
  477.      */
  478.     private int dstSavings; // field new in JDK 1.1.4
  479.  
  480.     /** 
  481.      * Constants specifying values of startMode and endMode.
  482.      */
  483.     private static final int DOM_MODE          = 1; // Exact day of month, "Mar 1"
  484.     private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
  485.     private static final int DOW_GE_DOM_MODE   = 3; // Day of week after day of month, "Sun>=15"
  486.     private static final int DOW_LE_DOM_MODE   = 4; // Day of week before day of month, "Sun<=21"
  487.  
  488.     // Proclaim compatibility with 1.1
  489.     static final long serialVersionUID = -403250971215465050L;
  490.  
  491.     // the internal serial version which says which version was written
  492.     // - 0 (default) for version up to JDK 1.1.3
  493.     // - 1 for version from JDK 1.1.4, which includes 3 new fields
  494.     static final int currentSerialVersion = 1;
  495.     private int serialVersionOnStream = currentSerialVersion;
  496.  
  497.     //----------------------------------------------------------------------
  498.     // Rule representation
  499.     //
  500.     // We represent the following flavors of rules:
  501.     //       5        the fifth of the month
  502.     //       lastSun  the last Sunday in the month
  503.     //       lastMon  the last Monday in the month
  504.     //       Sun>=8   first Sunday on or after the eighth
  505.     //       Sun<=25  last Sunday on or before the 25th
  506.     // This is further complicated by the fact that we need to remain
  507.     // backward compatible with the 1.1 FCS.  Finally, we need to minimize
  508.     // API changes.  In order to satisfy these requirements, we support
  509.     // three representation systems, and we translate between them.
  510.     //
  511.     // INTERNAL REPRESENTATION
  512.     // This is the format SimpleTimeZone objects take after construction or
  513.     // streaming in is complete.  Rules are represented directly, using an
  514.     // unencoded format.  We will discuss the start rule only below; the end
  515.     // rule is analogous.
  516.     //   startMode      Takes on enumerated values DAY_OF_MONTH,
  517.     //                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
  518.     //   startDay       The day of the month, or for DOW_IN_MONTH mode, a
  519.     //                  value indicating which DOW, such as +1 for first,
  520.     //                  +2 for second, -1 for last, etc.
  521.     //   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
  522.     //
  523.     // ENCODED REPRESENTATION
  524.     // This is the format accepted by the constructor and by setStartRule()
  525.     // and setEndRule().  It uses various combinations of positive, negative,
  526.     // and zero values to encode the different rules.  This representation
  527.     // allows us to specify all the different rule flavors without altering
  528.     // the API.
  529.     //   MODE              startMonth    startDay    startDayOfWeek
  530.     //   DOW_IN_MONTH_MODE >=0           !=0         >0
  531.     //   DOM_MODE          >=0           >0          ==0
  532.     //   DOW_GE_DOM_MODE   >=0           >0          <0
  533.     //   DOW_LE_DOM_MODE   >=0           <0          <0
  534.     //   (no DST)          don't care    ==0         don't care
  535.     //
  536.     // STREAMED REPRESENTATION
  537.     // We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
  538.     // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
  539.     // flag useDaylight.  When we stream an object out, we translate into an
  540.     // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
  541.     // and used by 1.1 code.  Following that, we write out the full
  542.     // representation separately so that contemporary code can recognize and
  543.     // parse it.  The full representation is written in a "packed" format,
  544.     // consisting of a version number, a length, and an array of bytes.  Future
  545.     // versions of this class may specify different versions.  If they wish to
  546.     // include additional data, they should do so by storing them after the
  547.     // packed representation below.
  548.     //----------------------------------------------------------------------
  549.  
  550.     /**
  551.      * Given a set of encoded rules in startDay and startDayOfMonth, decode
  552.      * them and set the startMode appropriately.  Do the same for endDay and
  553.      * endDayOfMonth.  Upon entry, the day of week variables may be zero or
  554.      * negative, in order to indicate special modes.  The day of month
  555.      * variables may also be negative.  Upon exit, the mode variables will be
  556.      * set, and the day of week and day of month variables will be positive.
  557.      * This method also recognizes a startDay or endDay of zero as indicating
  558.      * no DST.
  559.      */
  560.     private void decodeRules()
  561.     {
  562.         useDaylight = (startDay != 0) && (endDay != 0);
  563.  
  564.     if (startDayOfWeek == 0)
  565.         startMode = DOM_MODE;
  566.     else if (startDayOfWeek > 0)
  567.         startMode = DOW_IN_MONTH_MODE;
  568.     else
  569.     {
  570.         startDayOfWeek = -startDayOfWeek;
  571.         if (startDay > 0) startMode = DOW_GE_DOM_MODE;
  572.         else
  573.         {
  574.         startDay = -startDay;
  575.         startMode = DOW_LE_DOM_MODE;
  576.         }
  577.     }
  578.  
  579.     if (endDayOfWeek == 0)
  580.         endMode = DOM_MODE;
  581.     else if (endDayOfWeek > 0)
  582.         endMode = DOW_IN_MONTH_MODE;
  583.     else
  584.     {
  585.         endDayOfWeek = -endDayOfWeek;
  586.         if (endDay > 0) endMode = DOW_GE_DOM_MODE;
  587.         else
  588.         {
  589.         endDay = -endDay;
  590.         endMode = DOW_LE_DOM_MODE;
  591.         }
  592.     }
  593.     }
  594.  
  595.     /** 
  596.      * Make rules compatible to 1.1 FCS code.  Since 1.1 FCS code only understands
  597.      * day-of-week-in-month rules, we must modify other modes of rules to their
  598.      * approximate equivalent in 1.1 FCS terms.  This method is used when streaming
  599.      * out objects of this class.  After it is called, the rules will be modified,
  600.      * with a possible loss of information.  startMode and endMode will NOT be
  601.      * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
  602.      * since the rule modification is only intended to be temporary.
  603.      */
  604.     private void makeRulesCompatible()
  605.     {
  606.         switch (startMode)
  607.         {
  608.         case DOM_MODE:
  609.             startDay = 1 + (startDay / 7);
  610.             startDayOfWeek = Calendar.SUNDAY;
  611.             break;
  612.         case DOW_GE_DOM_MODE:
  613.             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  614.             // that is, Sun>=1 == firstSun.
  615.             if (startDay != 1)
  616.                 startDay = 1 + (startDay / 7);
  617.             break;
  618.         case DOW_LE_DOM_MODE:
  619.             if (startDay >= 30)
  620.                 startDay = -1;
  621.             else
  622.                 startDay = 1 + (startDay / 7);
  623.             break;
  624.         }
  625.  
  626.         switch (endMode)
  627.         {
  628.         case DOM_MODE:
  629.             endDay = 1 + (endDay / 7);
  630.             endDayOfWeek = Calendar.SUNDAY;
  631.             break;
  632.         case DOW_GE_DOM_MODE:
  633.             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  634.             // that is, Sun>=1 == firstSun.
  635.             if (endDay != 1)
  636.                 endDay = 1 + (endDay / 7);
  637.             break;
  638.         case DOW_LE_DOM_MODE:
  639.             if (endDay >= 30)
  640.                 endDay = -1;
  641.             else
  642.                 endDay = 1 + (endDay / 7);
  643.             break;
  644.         }
  645.     }
  646.  
  647.     /**
  648.      * Pack the start and end rules into an array of bytes.  Only pack
  649.      * data which is not preserved by makeRulesCompatible.
  650.      */
  651.     private byte[] packRules()
  652.     {
  653.         byte[] rules = new byte[4];
  654.         rules[0] = (byte)startDay;
  655.         rules[1] = (byte)startDayOfWeek;
  656.         rules[2] = (byte)endDay;
  657.         rules[3] = (byte)endDayOfWeek;
  658.         return rules;
  659.     }
  660.  
  661.     /**
  662.      * Given an array of bytes produced by packRules, interpret them
  663.      * as the start and end rules.
  664.      */
  665.     private void unpackRules(byte[] rules)
  666.     {
  667.         startDay       = rules[0];
  668.         startDayOfWeek = rules[1];
  669.         endDay         = rules[2];
  670.         endDayOfWeek   = rules[3];
  671.     }
  672.  
  673.     /**
  674.      * Write object out to a serialization stream.  Note that we write out two
  675.      * formats, a 1.1 FCS-compatible format, using DOW_IN_MONTH_MODE rules,
  676.      * in the "required" section, followed by the full rules, in packed format,
  677.      * in the "optional" section.  The optional section will be ignored by 1.1
  678.      * FCS code upon stream in.
  679.      */
  680.     private void writeObject(ObjectOutputStream stream)
  681.          throws IOException
  682.     {
  683.         // Construct a binary rule
  684.         byte[] rules = packRules();
  685.  
  686.         // Convert to 1.1 FCS rules.  This step may cause us to lose information.
  687.         makeRulesCompatible();
  688.  
  689.         // Write out the 1.1 FCS rules
  690.         stream.defaultWriteObject();
  691.  
  692.         // Write out the binary rules in the optional data area of the stream.
  693.         stream.writeInt(rules.length);
  694.         stream.write(rules);
  695.  
  696.         // Recover the original rules.  This recovers the information lost
  697.         // by makeRulesCompatible.
  698.         unpackRules(rules);
  699.     }
  700.  
  701.     /**
  702.      * Read this object out to a serialization stream.  We handle both 1.1 FCS
  703.      * binary formats, and full formats with a packed byte array.
  704.      */
  705.     private void readObject(ObjectInputStream stream)
  706.          throws IOException, ClassNotFoundException
  707.     {
  708.         stream.defaultReadObject();
  709.  
  710.         if (serialVersionOnStream < 1)
  711.         {
  712.             // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
  713.             // startDayOfWeek and endDayOfWeek were usually uninitialized.  We can't do
  714.             // too much, so we assume SUNDAY, which actually works most of the time.
  715.             if (startDayOfWeek == 0) startDayOfWeek = Calendar.SUNDAY;
  716.             if (endDayOfWeek == 0) endDayOfWeek = Calendar.SUNDAY;
  717.  
  718.             // The variables dstSavings, startMode, and endMode are post-1.1, so they
  719.             // won't be present if we're reading from a 1.1 stream.  Fix them up.
  720.             startMode = endMode = DOW_IN_MONTH_MODE;
  721.             dstSavings = millisPerHour;
  722.         }
  723.         else
  724.         {
  725.             // For 1.1.4, in addition to the 3 new instance variables, we also
  726.             // store the actual rules (which have not be made compatible with 1.1)
  727.             // in the optional area.  Read them in here and parse them.
  728.             int length = stream.readInt();
  729.             byte[] rules = new byte[length];
  730.             stream.readFully(rules);
  731.             unpackRules(rules);
  732.         }
  733.  
  734.         serialVersionOnStream = currentSerialVersion;
  735.     }
  736. }
  737.  
  738. //eof
  739.